package cn.tedu.charging.order.mqtt;

import cn.tedu.charging.common.constant.MqttDestinationConst;
import cn.tedu.charging.common.pojo.message.ChargingCheckResultMessage;
import cn.tedu.charging.common.pojo.message.ChargingProgressMsg;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallbackExtended;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttMessage;

import java.nio.charset.StandardCharsets;

/**
 * 即将交给MqttClient连接使用的回调对象
 */
@Slf4j
public class MqttMyCallback implements MqttCallbackExtended {
    private MqttClient client;
    private MqttContext mqttContext;

    //创建钩子函数 构造方法
    public MqttMyCallback(MqttClient client, MqttContext mqttContext) {
        this.client = client;
        this.mqttContext = mqttContext;
    }

    /**
     * 回调时机: 客户端mqttClient成功建立与emqx服务端连接关系时调用
     * boolean reconnect: true表示本次连接是重连,false表示第一次连接
     * String serverUri: 实际连接的地址 tcp://192.168.8.100:1883
     */
    @Override
    public void connectComplete(boolean reconnect, String serverURI) {
        if (reconnect) {
            //重连
            log.info("重新连接成功,broker地址:{}", serverURI);
        } else {
            log.info("首次连接成功,broker地址:{}", serverURI);
        }
        //让当前客户端MqttClient订阅需要消费的主题名称
        try {
            //根据需求定义监听订阅
            //订阅的是 充电桩 自检结果的主题 目的是接受 充电桩 返回给订单 的自检结果
            client.subscribe("$share/order/"+ MqttDestinationConst.GUN_CHECK_RESULT_TOPIC);

            //订阅的是 充电桩 同步充电进度的主题 目的是接受 充电桩 返回给订单 的充电进度
            client.subscribe("$share/order/"+ MqttDestinationConst.CHARGING_PROGRESS_TOPIC);
        } catch (Exception e) {
            log.error("订阅主题失败", e);
        }
    }

    /**
     * 回调时机: mqttClient连接断开时调用
     * Throwable cause: 连接断开原因
     * 如果设置了setAutomaticReconnect(true).自动重连,否则这个代码 手动处理重新连接
     */
    @Override
    public void connectionLost(Throwable cause) {
        log.info("连接断开", cause);
    }

    /**
     *消息到达 设备的消息 到达了 订单
     * 回调时机: 当前回调对象所属的MqttClient如果订阅了某些主题,如果主题中出现消息
     * 才会调用这个方法 客户端可以同时订阅多个主题
     * String topic: 消息来源主题
     * MqttMessage message: 消息对象(payLoad,qos retain)
     */
    @Override
    public void messageArrived(String topic, MqttMessage message) throws Exception {
        log.info("收到消息,来源:{}",topic);
        //判断消息来源 判断主题topic
        if(MqttDestinationConst.GUN_CHECK_RESULT_TOPIC.equals(topic)){
            log.info("收到充电桩自检结果");
            //订单处理 自检结果
            //1.当前方法负责 接收主题出现的消息
            //2.解析消息成为 数据封装类 调用业务
            ChargingCheckResultMessage resultMsg =
                    MqttProducerClient.deserializer(message.getPayload(),
                            ChargingCheckResultMessage.class);
            //从context中 拿到consumerService执行业务
            mqttContext.getMqttConsumerService().handleCheckResult(resultMsg);
        } else if (MqttDestinationConst.CHARGING_PROGRESS_TOPIC.equals(topic)) {
            //订单处理 充电进度
            log.info("收到充电进度");
            //充电桩同步的 json格式的 消息 message 转化为 java对象 ChargingProgressMsg
            ChargingProgressMsg progressMsg =
                    MqttProducerClient.deserializer(message.getPayload(),
                            ChargingProgressMsg.class);
            //处理充电进度消息
            mqttContext.getMqttConsumerService().handleProgress(progressMsg);
        }


    }

    /**
     * 回调时机: mqttClient发送publish消息的时候,如果成功了,收到emqx反馈ack=true就会调用
     * 这个方法,但是只有在QOS>0的时候才有效 因为  QOS=0 客户端不关心成功失败,不接收反馈
     * IMqttDeliveryToken token: 包含消息ID等上下文信息 原始消息就在token里
     */
    @Override
    public void deliveryComplete(IMqttDeliveryToken token) {
        log.info("消息发送成功,消息标识:{}", token.getMessageId());
    }
}
